/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package org.oimwrapper.miscadapters;

import Thor.API.Operations.*;
import Thor.API.tcResultSet;
import Thor.API.tcUtilityFactory;
import com.thortech.util.logging.Logger;
import com.thortech.xl.dataaccess.tcDataProvider;
import java.util.*;
import org.oimwrapper.connectors.internal.OIMLookupUtilities;
import org.oimwrapper.connectors.internal.OIMObjectFormUtilities;
import org.oimwrapper.connectors.internal.OIMUtilityGetter;

/**
 *
 */
public class ApprovalTasks {
    
    // field in the parent table that stores the key of the child group currently waiting for approval
    private static final String GROUPAPPROVALKEY = "GROUPAPPROVALKEY";
    
    private static Logger logger;
    private static Logger fwLogger = Logger.getLogger("FWLOGGER");
    
    private tcDataProvider database = null;
    private String taskName;
    private String taskStatus;
    private String objectInstanceKey;
    private String resourceName;
    
    private tcProvisioningOperationsIntf provOps = null;
    private tcFormInstanceOperationsIntf formInstOps = null;
    private tcFormDefinitionOperationsIntf formDefOps = null;
    private tcObjectOperationsIntf objectOps;
    private tcLookupOperationsIntf lookupOps;
    
    public ApprovalTasks() {
        logger = Logger.getLogger(ApprovalTasks.class.getName());
    }

    public ApprovalTasks(String LogCategory,tcDataProvider database) {
        logger = Logger.getLogger(LogCategory);
        this.database = database;
    }
    
    public void dummyMethod()
    {
        
    }
    
    public void setDatabase(tcDataProvider database)
    {
        this.database = database;
        //intializeOps();
    }
    
    public void setTaskName(String taskName)
    {
        this.taskName = taskName;
    }
    
    public void setObjectInstanceKey(String objectInstanceKey)
    {
        this.objectInstanceKey = objectInstanceKey;
    }
    
    public void setTaskStatus(String taskStatus)
    {
        this.taskStatus = taskStatus;
    }
    
    public void setResourcceName(String resourceName)
    {
        this.resourceName = resourceName;
    }
    
    public String intializeOps()
    {
        try
        {
            provOps = (tcProvisioningOperationsIntf) tcUtilityFactory.getUtility(database, "Thor.API.Operations.tcProvisioningOperationsIntf");
            formInstOps = (tcFormInstanceOperationsIntf)tcUtilityFactory.getUtility(database, "Thor.API.Operations.tcFormInstanceOperationsIntf");
            formDefOps = (tcFormDefinitionOperationsIntf)tcUtilityFactory.getUtility(database, "Thor.API.Operations.tcFormDefinitionOperationsIntf");
            objectOps = (tcObjectOperationsIntf)tcUtilityFactory.getUtility(database,"Thor.API.Operations.tcObjectOperationsIntf");
            lookupOps = (tcLookupOperationsIntf)tcUtilityFactory.getUtility(database, "Thor.API.Operations.tcLookupOperationsIntf");
        }
        catch(Exception e)
        {
            logger.error("Init Failed",e);
            return "FAILURE";
        }
        return "SUCCESS";
    }
    
    /*
     * find the last approver of a perticlar approval task and update the process form with that info.
     */
    public String setLastApprover()
    {
        
        long processInstanceKey = 0l;
        String taskKeyStr = null;
        String assignmentType = null;
        String assignmentId = null;
        
        logger.debug("setObjectInstanceKey " + objectInstanceKey);
        logger.debug("setTaskName " + taskName);
        
        if (Long.parseLong(objectInstanceKey) == 0l)
        {
            logger.error("Invalid Object Instance Key");
            return "SUCCESS";
        }
        
        
        try
        {
            processInstanceKey = getApprovalProcessInstanceKey(Long.parseLong(objectInstanceKey));
            logger.debug("ProcessInstanceKey " + processInstanceKey);
            long taskKey = getTaskKey(processInstanceKey,taskName,taskStatus);
            
            if (taskKey == 0l)
            {
                logger.error("Task Not Found for Task " + taskName + " with status " + taskStatus);
                return "Task Not Found for Task " + taskName + " with status " + taskStatus;
            }
            
            Map lastApproverMap = getLastTaskApprover(taskKey);
            
            assignmentType = (String)lastApproverMap.get("TYPE");
            assignmentId = (String)lastApproverMap.get("ID");
            
            logger.debug("Assignment=" + assignmentType + "/" + assignmentId);
            
            if (assignmentType == null || assignmentId == null)
            {
                logger.error("Task Assignment Not Found for Task " + taskName + " with status " + taskStatus);
                return "Task Assignment Not Found for Task " + taskName + " with status " + taskStatus;
            }
            updateLastApprover(Long.parseLong(objectInstanceKey),assignmentType,assignmentId);
        }
        catch(Exception e)
        {
            logger.error("Error Getting Tasks Complete",e);
            return "FAILURE";
            
        }
        return "SUCCESS";
        
    }
    
    /*
     * find the last approver of a task and update the matching the child record with that info.
     * 
     */
    public String setLastGroupApprover() {

        Map lastApproverMap = null;
        
        try {
            

            long processInstanceKey = getApprovalProcessInstanceKey(Long.parseLong(objectInstanceKey));
            long taskKey = getTaskKey(processInstanceKey, taskName, taskStatus);

            if (taskKey == 0l) {
                logger.error("Task Not Found for Task " + taskName + " with status " + taskStatus);
                return "Task Not Found for Task " + taskName + " with status " + taskStatus;
            }
            
            lastApproverMap = getLastTaskApprover(taskKey);
            
        } catch (Exception e) {
            logger.error("Error", e);
            return "FAILURE";
        }

        String assignmentType = (String) lastApproverMap.get("TYPE");
        String assignmentId = (String) lastApproverMap.get("ID");

        logger.debug("Assignment=" + assignmentType + "/" + assignmentId);

        if (assignmentType == null || assignmentId == null) {
            logger.error("Task Assignment Not Found for Task " + taskName + " with status " + taskStatus);
            return "Task Assignment Not Found for Task " + taskName + " with status " + taskStatus;
        }

        try
        {
            updateLastGroupApprover(Long.parseLong(objectInstanceKey),assignmentType,assignmentId);
        }
        catch(Exception e)
        {
            logger.error("Error",e);
            return "FAILURE";
        }
        // get the key to the approved child from the parent object form
        // find the matching child key
        // update the approver info in the child
        
        return "SUCCESS";
    }
    
    /*
     * check all the child approvers and see if one of them is in the provisioning groups
     * if so set that approvers info the parent object form
     */
    
    public String assignLastGroupApproverToParent()
    {
        logger.debug("assignLastGroupApproverToParent " + objectInstanceKey + ":" + resourceName);
        try
        {
            
            String lastApproverType = null;
            String lastApproverId = null;
            Stack groupApprovalList = getChildApprovers(Long.parseLong(objectInstanceKey),resourceName);
            if (groupApprovalList == null)
            {
                logger.debug("groupApprovalList is null. setting default");
                Map tmpMap = new HashMap();
                tmpMap.put("LASTAPPROVER_ID","SYSTEM ADMINISTRATORS");
                tmpMap.put("LASTAPPROVER_TYPE","group");
                groupApprovalList = new Stack();
                groupApprovalList.push(tmpMap);
            }
            List provisioningGroups = this.getProvisioningGroups();
            logger.debug("provisioningGroups:" + provisioningGroups);
            boolean haveApproval = false;
            for(int i=0;i<groupApprovalList.size();i++)
            {
                Map approval = (Map)groupApprovalList.pop();
                String approvalId = (String)approval.get("LASTAPPROVER_ID");
                logger.debug("Checking Approver Groups " + approvalId);
                if(provisioningGroups.contains(approvalId))
                {
                   logger.debug("Assigning " + approvalId);
                   lastApproverType = (String)approval.get("LASTAPPROVER_TYPE");
                   lastApproverId = approvalId;
                   haveApproval = true;
                   break;
                }
            }
            
            // child approval takes precidence over parent approval
            if (haveApproval)
            {
                // override defaults
                logger.debug("Child Approval Override");
                this.updateLastApprover(Long.parseLong(objectInstanceKey), lastApproverType, lastApproverId);
                
            }
            
            return "SUCCESS";
            
        }
        catch(Exception e)
        {
            logger.error("Error finding assignment",e);
            return "Error finding assignment " + e.getMessage();
        }
    }
    
    /*
     * get the values for the approver type and id that are in the object form
     * we re map them to our values
     */
    private Map getApprovalFields(long objectInstanceKey) throws Exception
    {
        logger.debug("getApprovalFields for OBI:" + objectInstanceKey);
        try
        {
            tcResultSet results = formInstOps.getObjectFormData(objectInstanceKey);
            if (results == null || results.getRowCount() == 0)
            {
                logger.error("No Record found for OBI:" + objectInstanceKey);
                return null;
            }
            
            String[] colNames = results.getColumnNames();
            HashMap fldMap = new HashMap();
            for(int i=0;i<results.getRowCount();i++)
            {
                
                results.goToRow(i);
                for(int c=0;c<colNames.length;c++)
                {
                    String n = colNames[c];
                    logger.debug("Checking Field:" + n);
                    if (n.contains("LASTAPPROVER_TYPE"))
                    {
                        fldMap.put("LASTAPPROVER_TYPE",results.getStringValue(n));
                    }
                    if (n.contains("LASTAPPROVER_ID"))
                    {
                        fldMap.put("LASTAPPROVER_ID",results.getStringValue(n));
                    }
                }
            }
            if (fldMap.isEmpty())
            {
                logger.error("No Approval fields found for OBI:" + objectInstanceKey);
                return null;
            }
            return fldMap;
            
        }
        catch(Exception e)
        {
            logger.error("Error Getting Approval Fields",e);
            throw e;
        }
        
    }
    
    /*
     * get all the approvers for the child records.
     */
    private Stack getChildApprovers(long objectInstanceKey,String resourceName) throws Exception
    {
        Stack approverList = new Stack();
        try
        {
            OIMObjectFormUtilities formUtils = OIMUtilityGetter.getObjectFormUtilities(fwLogger, objectOps, formInstOps, formDefOps);
            String childName = this.getChildFormNameForResource(resourceName);
            logger.debug("Get Object data for key " + objectInstanceKey);
            logger.debug("Resource " + resourceName + " childName " + childName);
            Map[] children = formUtils.getObjectFormChildValues(objectInstanceKey,childName,false, null);
            
            if (children == null || children.length == 0)
            {
                logger.error("No Record found for OBI:" + objectInstanceKey);
                return null;
            }
            
            String[] colNames = new String[children[0].keySet().size()];
            children[0].keySet().toArray(colNames);
            HashMap fldMap = new HashMap();
            for(int i=0;i<children.length;i++)
            {
                Map child = children[i];
                for(int c=0;c<colNames.length;c++)
                {
                    String n = colNames[c];
                    logger.debug("Checking Field:" + n);
                    if (n.contains("LASTAPPROVER_TYPE"))
                    {
                        fldMap.put("LASTAPPROVER_TYPE",child.get(n));
                    }
                    if (n.contains("LASTAPPROVER_ID"))
                    {
                        fldMap.put("LASTAPPROVER_ID",child.get(n));
                    }
                }
                if (!fldMap.isEmpty())
                {
                    approverList.push(fldMap);
                    fldMap = new HashMap();
                }
            }
            
            return approverList;
            
        }
        catch(Exception e)
        {
            logger.error("Error Getting Approval Fields",e);
            throw e;
        }
        
    }
    
    /*
     * update the child record with the last approval info. this chilc record should have a matching key in the
     * parent table.
     */
    private void updateLastGroupApprover(long objectInstanceKey,String assignmentType,String assignmentId) throws Exception {
        try {
            
            Map childKeyToUpdateMap = this.getGroupKeyForApproval(objectInstanceKey);
            
            String childKeyToUpdate = (String)childKeyToUpdateMap.get(GROUPAPPROVALKEY);
            
            if (childKeyToUpdate == null || childKeyToUpdate.trim().length() == 0)
            {
                logger.error("Error updateLastGroupApprover getting childKeyToUpdate " + objectInstanceKey);
                throw new Exception("Error updateLastGroupApprover getting childKeyToUpdate " + objectInstanceKey);
            }
            
            String childName = this.getChildFormNameForResource(resourceName);
            String childKeyField = childName + "_KEY";
            
            OIMObjectFormUtilities formUtils = OIMUtilityGetter.getObjectFormUtilities(fwLogger, objectOps,formInstOps, formDefOps);
            
            logger.debug("Getting Children for resource " + resourceName + " child " + childName);
            Map[] children = formUtils.getObjectFormChildValues(objectInstanceKey, childName, true, null);
            
            
            Set headers = null;
            
            if (children.length > 0) {
                headers = children[0].keySet();
            }
            if (headers == null)
            {
                logger.error("No Records updateLastGroupApprover " + objectInstanceKey + ":" + childName);
                throw new Exception("No Records updateLastGroupApprover");
            }

            Iterator i = headers.iterator();
            String typeFieldName = null;
            String idFieldName = null;
            
            while(i.hasNext())
            {
                String h = (String)i.next();
                if (h.contains("LASTAPPROVER_TYPE"))
                    typeFieldName = h;
                if (h.contains("LASTAPPROVER_ID"))
                    idFieldName = h;
                
            }
            
            
            HashMap udMap = new HashMap();
            udMap.put(typeFieldName,assignmentType);
            udMap.put(idFieldName,assignmentId);
            
            //String udField = null;
            logger.debug("Updating Child key " + childKeyToUpdate);

            for (int c = 0; c < children.length; c++) {
                
                Map rec = children[c];
                String childKey = (String)rec.get(childKeyField);
                if (childKeyToUpdate.equals(childKey))
                {
                    logger.debug("Updating Update Map" + udMap);
                    formUtils.updateObjectFormChildValue(objectInstanceKey, childName, Long.parseLong(childKeyToUpdate), udMap);
                    break;
                }
                
            }
            
        } catch (Exception e) {
            logger.error("Error", e);
            throw e;
        }

    }
    
    /*
     * update the parent record with the last approver info.
     */
    private void updateLastApprover(long objectInstanceKey,String assignmentType,String assignmentId) throws Exception {
        try {
            tcResultSet rs = formInstOps.getObjectFormData(objectInstanceKey);
            String[] headers = null;
            
            if (rs.getRowCount() > 0) {
                headers = rs.getColumnNames();
            }

            HashMap udMap = new HashMap();
            //String udField = null;
            logger.debug("finding LASTAPPROVER Field");

            for (int i = 0; i < rs.getRowCount(); i++) {
                rs.goToRow(i);
                for (int c = 0; c < headers.length; c++) {
                    //logger.debug(headers[c] + ":" + rs.getStringValue(headers[c]));
                    String tmp = headers[c];
                    if (tmp.contains("LASTAPPROVER_TYPE")) {
                        udMap.put(tmp, assignmentType);
                    }
                    if (tmp.contains("LASTAPPROVER_ID")) {
                        udMap.put(tmp, assignmentId);
                    }
                    //udMap.put(headers[c],rs.getStringValue(headers[c]));
                }
            }
            logger.debug("Checking Update Map" + udMap);
            if (!udMap.isEmpty()) {
                logger.debug("Updating Request Object ");
                //udMap.put(udField,assignment);
                formInstOps.setObjectFormData(objectInstanceKey, udMap);
            } else {
                logger.error("No LASTAPPROVER Field found");
            }
        } catch (Exception e) {
            logger.error("Error", e);
            throw e;
        }

    }
    
    /*
     * get the process instance key of the approval process for this object
     */
    private long getApprovalProcessInstanceKey(long objectInstanceKey) {
        logger.debug("Printing Approval Process Info");

        try {
            
            tcResultSet rs = provOps.getApprovalProcessInformation(objectInstanceKey);

            // hope that there is only one of these
            for (int i = 0; i < rs.getRowCount(); i++) {
                rs.goToRow(i);

                String procKey = rs.getStringValue("Process Instance.Key");
                if (procKey != null) {
                    return Long.parseLong(procKey);
                }
            }
        } catch (Exception e) {
            logger.error("Error getApprovalProcessInstanceKey", e);
        }
        return 0l;
    }
    
    /*
     * get the last task key of this task/status
     */
    private long getTaskKey(long processInstanceKey,String taskName,String taskStatus) {

        tcResultSet rs = null;
        String taskKeyStr = null;
        long taskKey = 0l;
        
        try {
            rs = provOps.getProcessDetail(processInstanceKey);
            for (int i = 0; i < rs.getRowCount(); i++) {
                rs.goToRow(i);


                String name = rs.getStringValue("Process Definition.Tasks.Task Name");
                String status = rs.getStringValue("Status.Category");

                if (taskName.equalsIgnoreCase(name) && taskStatus.equalsIgnoreCase(status)) {
                    taskKeyStr = rs.getStringValue("Process Instance.Task Details.Key");
                    if (taskKeyStr != null && taskKeyStr.length() > 0) {
                        //return Long.parseLong(taskKeyStr);
                        taskKey = Long.parseLong(taskKeyStr);
                    }
                }

            }
            return taskKey;
        } catch (Exception e) {
            logger.error("Error getTaskKey ",e);
        }
        return 0l;
    }
    
    /*
     * look thru the task history and find the last approver for this task.
     */
    private Map getLastTaskApprover(long taskKeyStr) {
        logger.debug("Printing Task History");

        String assignmentType = null;
        String assignmentId = null;
        Map returnMap = new HashMap();

        tcResultSet rs = null;
        try {
            rs = provOps.getTaskHistory(taskKeyStr);

            for (int i = 0; i < rs.getRowCount(); i++) {
                rs.goToRow(i);
                logger.debug("Printing Task History Row " + i);
                String type = rs.getStringValue("Process Instance.Task History.Assign Type");
                logger.debug("Type:" + type);
                if (type != null && type.equalsIgnoreCase("Group")) {
                    String groupName = rs.getStringValue("Groups.Group Name");
                    logger.debug("groupName:" + groupName);
                    if (groupName != null && groupName.trim().length() > 0) {
                        assignmentType = "group";
                        assignmentId = groupName;
                    }
                }
                if (type != null && type.equalsIgnoreCase("User")) {
                    String userName = rs.getStringValue("Process Instance.Task Information.Assignee User ID");
                    logger.debug("userName:" + userName);
                    if (userName != null && userName.trim().length() > 0) {
                        assignmentType = "user";
                        assignmentId = userName;
                    }
                }
            }
            
            if (assignmentType != null && assignmentId != null)
            {
                
                returnMap.put("TYPE",assignmentType);
                returnMap.put("ID",assignmentId);
            }
            
            return returnMap;
            
        } catch (Exception e) {
            logger.error("Error getLastTaskApprover",e);
            return null;
        }

    }
    
    /*
     * checks to see if there are any child records associated with this request that are missing approvals
     */
    public String checkGroupApprovalRequired()
    {
        logger.debug("checkGroupApprovalRequired");
        logger.debug("resource " + resourceName);
        logger.debug("obi " + objectInstanceKey);
        
        try
        {
            
            //rs = formInstOps.getObjectFormData(Long.parseLong(objectInstanceKey));
            
            String childName = getChildFormNameForResource(resourceName);
            logger.debug("Child Table " + childName);
            if (childName == null)
            {
                logger.error("Child Table not Found for resource " + resourceName);
                return "FAILURE";
            }
            
            OIMObjectFormUtilities formUtils = OIMUtilityGetter.getObjectFormUtilities(fwLogger, objectOps,formInstOps, formDefOps);
            
            Map[] reqChildren = formUtils.getObjectFormChildValues(Long.parseLong(objectInstanceKey), childName, true, null);
            String nextGroupToApprove = null;
            
            for(int c=0;c<reqChildren.length;c++)
            {
                Map childObj = reqChildren[c];
                Set<String> ckeys = childObj.keySet();
                Iterator<String> ci = ckeys.iterator();
                while(ci.hasNext())
                {
                    String k = ci.next();
                    String v = (String)childObj.get(k);
                    if (k.contains("LASTAPPROVER_ID"))
                    {
                        String lastId = v;
                        if (lastId == null || lastId.trim().length() == 0)
                        {
                            return "ASSIGNAPPROVER";
                        }
                    }
                    logger.debug(k + ":" + v);
                }
                
                
            }
            return "COMPLETE";
        }
        catch(Exception e)
        {
            logger.error("Error setNextGroupApproval",e);
        }
        return "FAILURE";
        
    }
    
    /*
     * lookup the child form name for this resource in the look up table.
     */
    private String getChildFormNameForResource(String resourceName)
    {
        try
        {
            
            OIMLookupUtilities lookupUtils = OIMUtilityGetter.getLookupUtils(fwLogger, lookupOps, database);
            String childName = lookupUtils.getLookupValue(resourceName, "Lookup.BofA.Resource.ChildObjectForms");
            return childName;
        }
        catch(Exception e)
        {
            logger.error("Error getChildFormNameForResource",e);
        }
        return null;
    }
    
    /*
     * get the key of the child record from the parent. this is the key of the child that is currently being approved
     */
    private Map getGroupKeyForApproval(long objectInstanceKey) throws Exception
    {
        try
        {
            tcResultSet results = formInstOps.getObjectFormData(objectInstanceKey);
            if (results == null || results.getRowCount() == 0)
            {
                logger.error("No Record found for OBI:" + objectInstanceKey);
                return null;
            }
            
            String[] colNames = results.getColumnNames();
            HashMap fldMap = new HashMap();
            for(int i=0;i<results.getRowCount();i++)
            {
                
                results.goToRow(i);
                for(int c=0;c<colNames.length;c++)
                {
                    String n = colNames[c];
                    logger.debug("Checking Field:" + n);
                    if (n.contains(GROUPAPPROVALKEY))
                    {
                        fldMap.put(GROUPAPPROVALKEY,results.getStringValue(n));
                    }
                }
            }
            if (fldMap.isEmpty())
            {
                logger.error("No Approval fields found for OBI:" + objectInstanceKey);
                return null;
            }
            return fldMap;
            
        }
        catch(Exception e)
        {
            logger.error("Error Getting Approval Fields",e);
            throw e;
        }
        
    }
    
    /*
     * get the cild form name for this resource via the lookup table
     */
    private List getProvisioningGroups() throws Exception
    {
        try
        {
            OIMLookupUtilities lookupUtils = OIMUtilityGetter.getLookupUtils(fwLogger, lookupOps, database);
            Map provisioningGroups = lookupUtils.getLookupValues("Lookup.BofA.Provisioning.Groups");
            Set<String> keys = provisioningGroups.keySet();
            
            List<String> provisioningList = new ArrayList<String>();
            
            for(String s : keys)
            {
                provisioningList.add(s);
            }
            return provisioningList;
        }
        catch(Exception e)
        {
            logger.error("Error getChildFormNameForResource",e);
            throw e;
        }
    }

}
